package signature

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"strings"
	"time"
)

// 请求签名结构体
type RequestSignature struct {
	Method  string           // 请求方法
	Uri     string           // 请求URI
	Params  []RequestParams  // 请求参数, 按照键名排序
	Headers []RequestHeaders // 请求头, 按照键名排序
	Body    []byte           // 请求体

	AccessKey string // 访问密钥
	Secret    string // 密钥

	Version   string // 版本号
	Algorithm string // 算法
}

// 创建请求签名对象
func NewDefaultRequestSignature(method string, uri string, params []RequestParams, headers []RequestHeaders, body []byte) *RequestSignature {
	return &RequestSignature{
		Method:  method,
		Uri:     uri,
		Params:  params,
		Headers: headers,
		Body:    body,

		AccessKey: AccessKey,
		Secret:    Secret,

		Version:   Version,
		Algorithm: Algorithm,
	}
}

// 生成签名授权字符串
func (r *RequestSignature) GenAuthorization() string {
	Credentials := fmt.Sprintf("Credentials=%s", r.AccessKey)
	SignedHeaders := fmt.Sprintf("SignedHeaders=%s", r.genHeaderKeys())
	Signature := fmt.Sprintf("Signature=%s", r.sign())
	Timestamp := fmt.Sprintf("Timestamp=%s", r.genTimeStamp())

	return fmt.Sprintf("%s %s", r.genVersionAlgorithm(), strings.Join([]string{Credentials, SignedHeaders, Signature, Timestamp}, ","))
}

// 计算签名
func (r *RequestSignature) sign() string {
	versionAlgorithm := r.genVersionAlgorithm()
	timeStamp := r.genTimeStamp()
	signStr := r.genSignString()

	content := []string{versionAlgorithm, timeStamp, signStr}

	hashed := hmac.New(sha256.New, []byte(r.Secret))
	contentText := strings.Join(content, "\n")
	fmt.Println("=============签名============")
	fmt.Println(contentText)
	fmt.Println("============================")
	hashed.Write([]byte(contentText))
	hexHash := hex.EncodeToString(hashed.Sum(nil))
	return hexHash
}

// 生成签名字符串
func (r *RequestSignature) genSignString() string {
	var content []string

	if len(r.Headers) > 0 {
		if r.genParams() == "" {
			content = append(content, r.Method, r.Uri, r.genHeaders(), r.genBody())
		} else {
			content = append(content, r.Method, r.Uri, r.genParams(), r.genHeaders(), r.genBody())
		}
	} else {
		if r.genParams() == "" {
			content = append(content, r.Method, r.Uri, r.genHeaders(), r.genBody())
		} else {
			content = append(content, r.Method, r.Uri, r.genParams(), r.genBody())
		}
	}

	join := strings.Join(content, "\n")
	fmt.Println("=============规范请求=============")
	fmt.Println(join)
	fmt.Println("=================================")
	return fmt.Sprintf("%x", sha256.Sum256([]byte(join)))
}

// 生成请求参数字符串
func (r *RequestSignature) genParams() string {
	var params []string

	for _, v := range r.Params {
		params = append(params, v.Key+"="+v.Value)
	}

	return strings.Join(params, "&")
}

// 生成请求头字符串
func (r *RequestSignature) genHeaders() string {
	if len(r.Headers) == 0 {
		return ""
	}

	var headerKeys, headers []string
	for _, v := range r.Headers {
		headerKeys = append(headerKeys, v.Key)
		headers = append(headers, v.Key+":"+v.Value)
	}

	return strings.Join(headers, "\n") + "\n\n" + strings.Join(headerKeys, ";")
}

// 生成请求头键名字符串
func (r *RequestSignature) genHeaderKeys() string {
	if len(r.Headers) == 0 {
		return ""
	}

	var headerKeys []string
	for _, v := range r.Headers {
		headerKeys = append(headerKeys, v.Key)
	}

	return strings.Join(headerKeys, ";")
}

// 生成请求体字符串
func (r *RequestSignature) genBody() string {
	return fmt.Sprintf("%x", sha256.Sum256(r.Body))
}

// 生成时间戳
func (r *RequestSignature) genTimeStamp() string {
	return time.Now().UTC().Format("20060102T150405Z")
}

// 生成版本与算法字符串
func (r *RequestSignature) genVersionAlgorithm() string {
	return fmt.Sprintf("%s-%s", r.Version, r.Algorithm)
}
